//===============================================================================
// Microsoft patterns & practices
// Guidance Explorer Offline Client
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
//using GuidanceExplorer.WorkItems;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;
using GuidanceExplorer.Model;
using GuidanceExplorer.Schemas;
using Forms = System.Windows.Forms;

namespace GuidanceExplorer.Services
{
    public class ModelStorageService : IModelStorageService
    {
        protected static string libraryFolder = @"..\Libraries";
		protected static string templateFolder = @"..\Templates";
        protected static string guidanceFolderName = "GuidanceExplorer"; 
		public static string LibraryFolder
		{
			get 
            {
                if (Path.IsPathRooted(libraryFolder))
                {
                    return libraryFolder;
                }
                return Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), libraryFolder);
            }
		}
		public static string TemplateFolder
		{
			get 
            {
                if (Path.IsPathRooted(templateFolder))
                {
                    return templateFolder;
                }
                return Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), templateFolder);
            }
		}

        public const string DefaultCSSFileName = "guidance.css";
        public const string LibrariesColumnsConfiguration = "librariesColumns.dat";
        public const string GuidanceTypesFile = "GuidanceTypes.xml";
        protected enum TypeOfLibrary { Library, UserViews, GlobalViews };
        public const int ItemNameMaxLength = 45;

        /// <summary>
        /// The previously loaded guidance instance.
        /// </summary>
        protected Guidance _guidance;

        /// <summary>
        /// Load guidance and return the intance.
        /// </summary>
        /// <returns>An instance of the Guidance loaded from file.</returns>
        public Guidance LoadGuidance(string currentDirectory, ClientFlavour clientFlavour)
        {
            //Create the guidance type filename
            string guidanceTypeFileName = Path.GetFullPath(Path.Combine(currentDirectory, GuidanceTypesFile));

            //Load all guidance type information
            LoadGuidanceTypes(new FileInfo(guidanceTypeFileName));

            // Creates the guidance support.
            _guidance = Guidance.New();
            _guidance.ClientFlavour = clientFlavour;

            string libraryBaseFolder = Path.GetFullPath(Path.Combine(currentDirectory, LibraryFolder));
            if (Directory.Exists(libraryBaseFolder))
            {
                string librariesColumns = Path.Combine(libraryBaseFolder, LibrariesColumnsConfiguration);

                if (File.Exists(librariesColumns))
                {
                    using (XmlReader reader = XmlTextReader.Create(librariesColumns))
                    {
                        _guidance.Columns = ConvertXmlReaderToColumns(reader);
                    }
                }

                foreach (string file in Directory.GetFiles(libraryBaseFolder, "*.xml"))
                {
                    FileInfo fileInfo = new FileInfo(file);
                    if (TypeOfLibraryFile(fileInfo) == TypeOfLibrary.Library)
                    {
                        try
                        {
                            Library library = ConvertFileInfoToLibrary(fileInfo);
                            _guidance.Libraries.Add(library.Name, library);
                        }
                        catch (IOException ioEx)
                        {
                            Forms.MessageBox.Show(ioEx.Message, Messages.ERROR, Forms.MessageBoxButtons.OK, Forms.MessageBoxIcon.Error);
                        }
                        catch (Exception)//If the library load fails, ignore that library
                        { }
                    }
                    else if (TypeOfLibraryFile(fileInfo) == TypeOfLibrary.UserViews)
                    {
                        UserViews userViews = ConvertFileInfoToUserViews(fileInfo);
                        _guidance.UserViews = userViews;
                    }
                    else if (TypeOfLibraryFile(fileInfo) == TypeOfLibrary.GlobalViews)
                    {
                        GlobalViews globalViews = ConvertFileInfoToGlobalViews(fileInfo);
                        _guidance.GlobalViews = globalViews;
                    }
                }
            }

            return _guidance;
        }

        /// <summary>
        /// Return the previously loaded guidance.
        /// </summary>
        public Guidance Guidance
        {
            get { return _guidance; }
        }

        /// <summary>
        /// Covnverts a FileInfo into a Library
        /// </summary>
        /// <param name="fileInfo">The instance of the file info the convert.</param>
        /// <returns>The new Library instance.</returns>
        public Library ConvertFileInfoToLibrary(FileInfo fileInfo)
        {
            //Prepares the XmlReader.
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ValidationType = ValidationType.Schema;
            settings.ValidationEventHandler += new ValidationEventHandler(
                delegate(object sender, ValidationEventArgs args)
                {
                    throw args.Exception;
                }
            );

            //Load the XmlSchema from the embedded resources.
            XmlSchema schema = XmlSchema.Read(Assembly.GetExecutingAssembly().GetManifestResourceStream("GuidanceExplorer.Schemas.Library.xsd"), null);
            settings.Schemas.Add(schema);

            // Opens the file.
            using (FileStream fileStream = fileInfo.Open(FileMode.Open, FileAccess.Read))
            // Instance a reader from the file.
            using (XmlReader reader = XmlTextReader.Create(fileStream, settings))
            {
                //Use the converter service to process the XmlReader.
                Library library = ConvertXmlReaderToLibrary(reader);
                library.SourceFileInfo = fileInfo;
                return library;
            }
        }
		/// <summary>
		/// Detects what type of library file this is.
		/// </summary>
		/// <param name="fileInfo">The file to test.</param>
		/// <returns><c>true</c> Type of library file, otherwise 
		/// an exception is thrown.</returns>
		protected TypeOfLibrary TypeOfLibraryFile(FileInfo fileInfo)
		{
			//Prepares the XmlReader.
			XmlReaderSettings settings = new XmlReaderSettings();
			settings.ValidationType = ValidationType.Schema;
			settings.ValidationEventHandler += new ValidationEventHandler(
				delegate(object sender, ValidationEventArgs args)
				{
					throw args.Exception;
				}
			);

			//Load the XmlSchema from the embedded resources.
            XmlSchema schema = XmlSchema.Read(Assembly.GetExecutingAssembly().GetManifestResourceStream("GuidanceExplorer.Schemas.Library.xsd"), null);
            settings.Schemas.Add(schema);

			// Opens the file.
			using (FileStream fileStream = fileInfo.Open(FileMode.Open, FileAccess.Read))
			// Instance a reader from the file.
			using (XmlReader reader = XmlTextReader.Create(fileStream, settings))
			{
				while (reader.Read())
				{
					if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Library.Element))
					{
						return TypeOfLibrary.Library;
					}
					else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.UserViews.Element))
					{
						return TypeOfLibrary.UserViews;
					}
					else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.GlobalViews.Element))
					{
						return TypeOfLibrary.GlobalViews;
					}
				}
			}
			throw new Exception(string.Format("Invalid file {0}.", fileInfo.Name));
		}

        /// <summary>
        /// Converts an instance of and XmlReader into an instace of a Library.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        private Library ConvertXmlReaderToLibrary(XmlReader reader)
        {
            Library library = Library.New();
            // Read the file through
            while (reader.Read())
            {
                // Find the library node.
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Library.Element))
                {
                    //Process all the attributes.
                    if (reader.MoveToFirstAttribute())
                    {
                        do
                        {
                            if (reader.Name == SchemaHelper.Library.Name)
                            {
                                Guid guid;
                                try
                                {
                                    guid = new Guid(reader.Value);
                                }
                                catch
                                {
                                    guid = Guid.NewGuid();
                                }

                                library.Name = guid.ToString();
                            }
                            else if (reader.Name == SchemaHelper.Library.Caption)
                            {
                                library.Caption = reader.Value;
                            }
                            else if (reader.Name == SchemaHelper.Library.ReadProtection)
                            {
                                library.ReadProtection = (ReadProtection)Enum.Parse(typeof(ReadProtection), reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Library.FileSubscribed)
                            {
                                library.FileSubscribed = XmlConvert.ToBoolean(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Library.WebSubscribed)
                            {
                                library.WebSubscribed = XmlConvert.ToBoolean(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Library.URL)
                            {
                                library.URL = reader.Value;
                            }
                            else if (reader.Name == SchemaHelper.Library.OriginalSource)
                            {
                                library.OriginalSourceFileInfo = new FileInfo(reader.Value);
                            }
                        } while (reader.MoveToNextAttribute());
                    }
                }
                // Find the Library structure
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.LibraryStructure.Element))
                {
                    ProcessLibraryStructure(library, reader.ReadSubtree());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Columns.Element))
                {
                    library.Columns = ConvertXmlReaderToColumns(reader.ReadSubtree());
                }
                // Find the items element
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Items.Element))
                {
                    GuidanceItemFileReferenceCollection items = ConvertXmlReaderToGuidanceItemFileReference(reader.ReadSubtree());
                    foreach (GuidanceItemFileReference item in items.Values)
                    {
                        library.Items.Add(item.Id, item);
                    }
                }
            }

            //Load the subscribed library.
            if (library.FileSubscribed && library.OriginalSourceFileInfo != null)
            {
                try
                {
                    FileInfo originalFileInfo = library.OriginalSourceFileInfo;
                    library = ConvertFileInfoToLibrary(library.OriginalSourceFileInfo);
                    library.FileSubscribed = true;
                    //library.ReadProtection = ReadProtection.ReadOnly;
                    library.OriginalSourceFileInfo = originalFileInfo;
                }
                catch (IOException)
                {
                    //throw new IOException(string.Format(Messages.CANNOT_LOAD_LIBRARY, library.OriginalSourceFileInfo));
                    //Set a Library node with read-only protection

                    library.ReadProtection = ReadProtection.ReadOnly;
                    library.RenderAsFile = false;
                }
                catch (UnauthorizedAccessException)
                {
                    //throw new UnauthorizedAccessException(string.Format(Messages.CANNOT_LOAD_LIBRARY, library.OriginalSourceFileInfo));
                    //Set a Library node with read-only protection

                    library.ReadProtection = ReadProtection.ReadOnly;
                    library.RenderAsFile = false;
                }
            }

            return library;
        }

        /// <summary>
        /// Convert an instance of an XmlReader positioned at the userViews node into an instance of UserViews.
        /// </summary>
        /// <param name="reader">The XmlReader positioned at the userViews.</param>
        /// <returns>A new instance of UserViews.</returns>
        private UserViews ConvertXmlReaderToUserViews(XmlReader reader)
        {
            //Create the user views
            UserViews userViews = UserViews.New();

            // Read the file through
            while (reader.Read())
            {
                // Find the library node.
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.UserViews.Structure))
                {
                    ProcessLibraryStructure(userViews, reader.ReadSubtree());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Columns.Element))
                {
                    userViews.Columns = ConvertXmlReaderToColumns(reader.ReadSubtree());
                }
            }
            return userViews;
        }

        /// <summary>
        /// Convert an instance of an XmlReader into an instance of GlobalViews.
        /// </summary>
        /// <param name="reader">The XmlReader.</param>
        /// <returns>A new instance of GlobalViews.</returns>
        private GlobalViews ConvertXmlReaderToGlobalViews(XmlReader reader)
        {
            //Create the user views
            GlobalViews globalViews = GlobalViews.New();

            // Read the file through
            while (reader.Read())
            {
                // Find the library node.
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.GlobalViews.Structure))
                {
                    ProcessLibraryStructure(globalViews, reader.ReadSubtree());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Columns.Element))
                {
                    globalViews.Columns = ConvertXmlReaderToColumns(reader.ReadSubtree());
                }
            }
            return globalViews;
        }
        
        /// <summary>
        /// Load the guidance types from the file and set its properties.
        /// </summary>
        /// <param name="guidanceTypeFile">The file used to load guidance type information.</param>
        public void LoadGuidanceTypes(FileInfo guidanceTypeFile)
        {
            //TODO: validate the schema.
            using (FileStream fileStream = new FileStream(guidanceTypeFile.FullName, FileMode.Open, FileAccess.Read))
            using (XmlReader reader = XmlTextReader.Create(fileStream))
            {
                //Read the entire file.
                while (reader.Read())
                {
                    if (SchemaHelper.IsGuidanceTypesNode(reader, SchemaHelper.GuidanceTypes.Element))
                    {
                        //Read the entire file.
                        while (reader.Read())
                        {
                            if (SchemaHelper.IsGuidanceTypesNode(reader, SchemaHelper.GuidanceType.Element))
                            {
                                //Prepare the variables for loading the data
                                string displayName = string.Empty, listDisplayName = string.Empty;
                                string name = string.Empty, newGuidanceTypeDisplayName = string.Empty;
                                string templateFileName = string.Empty, imageFileName = string.Empty;
                                string exampleFileName = string.Empty, nodeDisplayName = string.Empty;
                                string Id = string.Empty;
                                string schemaId = string.Empty;
                                bool isDeleted = false;
                                DateTime lastUpdated = DateTime.MinValue;

                                if (reader.MoveToFirstAttribute())
                                {
                                    do
                                    {
                                        if (reader.Name == SchemaHelper.GuidanceType.Id)
                                        {
                                            Id = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.DisplayName)
                                        {
                                            displayName = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.SchemaId)
                                        {
                                            schemaId = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.ListDisplayName)
                                        {
                                            listDisplayName = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.Name)
                                        {
                                            name = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.NewGuidanceItemDisplayName)
                                        {
                                            newGuidanceTypeDisplayName = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.ImageFileName)
                                        {
                                            imageFileName = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.TemplateFileName)
                                        {
                                            templateFileName = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.ExampleFileName)
                                        {
                                            exampleFileName = reader.Value;
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.CreationDate)
                                        {
                                            lastUpdated = XmlConvert.ToDateTime(reader.Value, XmlDateTimeSerializationMode.Utc);
                                            if (lastUpdated == DateTime.MinValue)
                                            {
                                                lastUpdated = DateTime.UtcNow;
                                            }
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.IsDeleted)
                                        {
                                            isDeleted = XmlConvert.ToBoolean(reader.Value);
                                        }
                                        else if (reader.Name == SchemaHelper.GuidanceType.NodeDisplayName)
                                        {
                                            nodeDisplayName = reader.Value;
                                        }

                                    } while (reader.MoveToNextAttribute());
                                    reader.MoveToElement();
                                }

                                //Create the instance of the GuidanceType and set its properties.
                                GuidanceType guidanceType = GuidanceType.New(name);
                                guidanceType.Id = Id;
                                guidanceType.SchemaId = schemaId;
                                guidanceType.Name = name;
                                guidanceType.DisplayName = displayName;
                                guidanceType.ListDisplayName = listDisplayName;
                                guidanceType.NewGuidanceTypeDisplayName = newGuidanceTypeDisplayName;
                                guidanceType.TemplateFileName = String.IsNullOrEmpty(templateFileName) ? (name + "_template.xml") : templateFileName;
                                guidanceType.ImageFileName = String.IsNullOrEmpty(imageFileName) ? (name + ".bmp") : imageFileName;
                                guidanceType.ExampleFileName = String.IsNullOrEmpty(exampleFileName) ? (name + "_example.xml") : exampleFileName;
                                guidanceType.DateTime = lastUpdated;
                                guidanceType.IsDeleted = isDeleted;
                                guidanceType.NodeDisplayName = nodeDisplayName;

                                //Read the field definitions.
                                LoadGuidanceTypeFields(reader.ReadSubtree(), guidanceType);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Load the field definition of the guidance type.
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="guidanceType"></param>
        private void LoadGuidanceTypeFields(XmlReader reader, GuidanceType guidanceType)
        {
            //Read the entire xml element.
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceTypesNode(reader, SchemaHelper.Fields.Element))
                {
                }
                else if (SchemaHelper.IsGuidanceTypesNode(reader, SchemaHelper.Field.Element))
                {
                    Field field = Field.New();
                    bool positionSetted = false;
                    if (reader.MoveToFirstAttribute())
                    {
                        do
                        {
                            if (reader.Name == SchemaHelper.Field.Name)
                            {
                                field.Name = reader.Value;
                            }
                            else if (reader.Name == SchemaHelper.Field.Caption)
                            {
                                field.Caption = reader.Value;
                            }
                            else if (reader.Name == SchemaHelper.Field.Type)
                            {
                                field.Type = (FieldType)Enum.Parse(typeof(FieldType), reader.Value, true);
                            }
                            else if (reader.Name == SchemaHelper.Field.DefaultSort)
                            {
                                field.DefaultSort = (SortOrder)Enum.Parse(typeof(SortOrder), reader.Value, true);
                            }
                            else if (reader.Name == SchemaHelper.Field.Distinct)
                            {
                                field.Distinct = XmlConvert.ToBoolean(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Field.Editable)
                            {
                                field.Editable = XmlConvert.ToBoolean(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Field.ShowInGuidanceTypeNode)
                            {
                                field.ShowInGuidanceTypeNode = XmlConvert.ToBoolean(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Field.Visible)
                            {
                                field.Visible = XmlConvert.ToBoolean(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Field.Values)
                            {
                                field.Values = reader.Value.Split(',');
                            }
                            else if (reader.Name == SchemaHelper.Field.Width)
                            {
                                int temp;
                                int.TryParse(reader.Value, out temp);

                                if (temp > 0)
                                {
                                    field.Width = temp;
                                }
                            }
                            else if (reader.Name == SchemaHelper.Field.Position)
                            {
                                int temp;
                                int.TryParse(reader.Value, out temp);

                                if (temp >= 0)
                                {
                                    field.Position = temp;
                                    positionSetted = true;
                                }
                            }
                        } while (reader.MoveToNextAttribute());
                        reader.MoveToElement();
                    }

                    //If the position is not setted
                    if (!positionSetted)
                    {
                        field.Position = guidanceType.Fields.Count;
                    }

                    //If the caption has not been set, then use the name.
                    if (string.IsNullOrEmpty(field.Caption))
                    {
                        field.Caption = field.Name;
                    }

                    //Add the field or replace it when it was already loaded.
                    if (guidanceType.Fields.ContainsKey(field.Name))
                    {
                        guidanceType.Fields[field.Name] = field;
                    }
                    else
                    {
                        guidanceType.Fields.Add(field.Name, field);
                    }
                }
            }
        }

        /// <summary>
        /// Converts a FileInfo into a Library
        /// </summary>
        /// <param name="fileInfo">The instance of the file info the convert.</param>
        /// <returns>The new Library instance.</returns>
        public UserViews ConvertFileInfoToUserViews(FileInfo fileInfo)
        {
            //Prepares the XmlReader.
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ValidationType = ValidationType.Schema;
            settings.ValidationEventHandler += new ValidationEventHandler(
                delegate(object sender, ValidationEventArgs args)
                {
                    throw args.Exception;
                }
            );

            //Load the XmlSchema from the embedded resources.
            XmlSchema schema = XmlSchema.Read(Assembly.GetExecutingAssembly().GetManifestResourceStream("GuidanceExplorer.Schemas.Library.xsd"), null);
            settings.Schemas.Add(schema);

            // Opens the file.
            using (FileStream fileStream = fileInfo.Open(FileMode.Open, FileAccess.Read))
            // Instance a reader from the file.
            using (XmlReader reader = XmlTextReader.Create(fileStream, settings))
            {
                //Use the converter service to process the XmlReader.
                UserViews userViews = ConvertXmlReaderToUserViews(reader);
                userViews.SourceFileInfo = fileInfo;
                return userViews;
            }
        }

        /// <summary>
        /// Converts a FileInfo into a Library
        /// </summary>
        /// <param name="fileInfo">The instance of the file info the convert.</param>
        /// <returns>The new Library instance.</returns>
        public GlobalViews ConvertFileInfoToGlobalViews(FileInfo fileInfo)
        {
            //Prepares the XmlReader.
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ValidationType = ValidationType.Schema;
            settings.ValidationEventHandler += new ValidationEventHandler(
                delegate(object sender, ValidationEventArgs args)
                {
                    throw args.Exception;
                }
            );

            //Load the XmlSchema from the embedded resources.
            XmlSchema schema = XmlSchema.Read(Assembly.GetExecutingAssembly().GetManifestResourceStream("GuidanceExplorer.Schemas.Library.xsd"), null);
            settings.Schemas.Add(schema);

            // Opens the file.
            using (FileStream fileStream = fileInfo.Open(FileMode.Open, FileAccess.Read))
            // Instance a reader from the file.
            using (XmlReader reader = XmlTextReader.Create(fileStream, settings))
            {
                //Use the converter service to process the XmlReader.
                GlobalViews globalViews = ConvertXmlReaderToGlobalViews(reader);
                globalViews.SourceFileInfo = fileInfo;
                return globalViews;
            }
        }

        /// <summary>
        /// Process the library structure node
        /// </summary>
        /// <param name="structure">The folder structure (Library or UserViews) instance.</param>
        /// <param name="reader">The xml reader instance.</param>
        protected void ProcessLibraryStructure(FolderStructure structure, XmlReader reader)
        {
            //Read the entire section
            while (reader.Read())
            {
                //Find the Folder element
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Folder.Element))
                {
                    Folder folder = ConvertXmlReaderToFolder(reader.ReadSubtree());
                    structure.Folders.Add(folder.Caption, folder);
                    folder.Container = structure;
                }
                //Find the View element
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.View.Element))
                {
                    GuidanceExplorer.Model.View view = ConvertXmlReaderToView(reader.ReadSubtree());
                    structure.Views.Add(view.Caption, view);
                    view.Container = structure;
                }
                //Find the SearchView element
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.SearchView.Element))
                {
                    SearchView view = ConvertXmlReaderToSearchView(reader.ReadSubtree());
                    structure.Views.Add(view.Caption, view);
                    view.Container = structure;
                }
            }
        }

        /// <summary>
        /// Converts the current reader into a new instance of a SearchView.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        private SearchView ConvertXmlReaderToSearchView(XmlReader reader)
        {
            //Create a new view.
            SearchView newSearchView = SearchView.New();

            //Read the attributes.
            if (reader.Read())
            {
                if (reader.MoveToFirstAttribute())
                {
                    do
                    {
                        if (reader.Name == SchemaHelper.SearchView.Caption)
                        {
                            newSearchView.Caption = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.SearchView.Id)
                        {
                            newSearchView.Id = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.SearchView.Author)
                        {
                            newSearchView.Author = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.SearchView.Description)
                        {
                            newSearchView.Description = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.SearchView.CreationDate)
                        {
                            newSearchView.LastUpdate = XmlConvert.ToDateTime(reader.Value, XmlDateTimeSerializationMode.Utc);
                        }
                        else if (reader.Name == SchemaHelper.SearchView.ContainerScopeType)
                        {
                            newSearchView.ReadedScope = (SearchViewScope)Enum.Parse(typeof(SearchViewScope), reader.Value);
                        }
                        else if (reader.Name == SchemaHelper.SearchView.ContainerScope)
                        {
                            newSearchView.ReadedScopeName = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.SearchView.RootView)
                        {
                            newSearchView.RootView = XmlConvert.ToBoolean(reader.Value);
                        }
                    } while (reader.MoveToNextAttribute());
                }
            }

            //Read the inner nodes.
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Columns.Element))
                {
                    newSearchView.Columns = ConvertXmlReaderToColumns(reader.ReadSubtree());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Criteria.Element))
                {
                    newSearchView.Criteria = ConvertXmlReaderToCriteria(reader.ReadSubtree());
                }
            }

            newSearchView.ContainerScope = _guidance;

            return newSearchView;
        }

        private List<Criteria> ConvertXmlReaderToListOfCriterias(XmlReader reader)
        {
            List<Criteria> criterias = new List<Criteria>();
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(FieldSearchCriteria).Name))
                {
                    Field field = null;
                    string value = null;
                    SearchTerms searchTerm = SearchTerms.Contains;
                    if (reader.MoveToAttribute(SchemaHelper.FieldSearchCriteria.Field))
                    {
                        foreach (GuidanceType type in GuidanceType.AllGuidanceTypes)
                        {
                            foreach (Field f in type.Fields.Values)
                            {
                                if (string.Compare(f.Name, reader.Value, true) == 0)
                                {
                                    field = f;
                                    break;
                                }
                            }
                        }
                    }

                    if (reader.MoveToAttribute(SchemaHelper.FieldSearchCriteria.SearchTerm))
                    {
                        searchTerm = (SearchTerms)Enum.Parse(typeof(SearchTerms), reader.Value);
                    }

                    if (reader.MoveToAttribute(SchemaHelper.FieldSearchCriteria.Value))
                    {
                        value = reader.Value;
                    }

                    criterias.Add(new FieldSearchCriteria(field, value, searchTerm, _guidance));
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(TrueSearchCriteria).Name))
                {
                    criterias.Add(new TrueSearchCriteria());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(FullTextCriteria).Name))
                {
                    criterias.Add(new FullTextCriteria(_guidance, reader.ReadElementContentAsString()));
                }
            }

            return criterias;
        }

        /// <summary>
        /// Convert the xml in the reader to a new instance of a Criteria.
        /// </summary>
        /// <param name="reader"></param>
        /// <returns></returns>
        public Criteria ConvertXmlReaderToCriteria(XmlReader reader)
        {
            Criteria criteria = null;
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Criteria.Element))
                {
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(FullTextCriteria).Name))
                {
                    criteria = new FullTextCriteria(_guidance, reader.ReadElementContentAsString());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(AndSearchCriteria).Name))
                {
                    criteria = new AndSearchCriteria(ConvertXmlReaderToListOfCriterias(reader));
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(OrSearchCriteria).Name))
                {
                    criteria = new OrSearchCriteria(ConvertXmlReaderToListOfCriterias(reader));
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(TrueSearchCriteria).Name))
                {
                    criteria = new TrueSearchCriteria();
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, typeof(TopSearchCriteria).Name))
                {
                    int top = 25;
                    if (reader.MoveToFirstAttribute())
                    {
                        top = int.Parse(reader.Value);
                        reader.MoveToElement();
                    }
                    criteria = new TopSearchCriteria(ConvertXmlReaderToListOfCriterias(reader), top);
                }
            }
            return criteria;
        }

        /// <summary>
        /// Process the items node in the library node. File references will be found in the items node.
        /// </summary>
        /// <param name="xmlReader">The xml reader positioned at the Items node.</param>
        private GuidanceItemFileReferenceCollection ConvertXmlReaderToGuidanceItemFileReference(XmlReader reader)
        {
            //Create a new collection.
            GuidanceItemFileReferenceCollection items = GuidanceItemFileReferenceCollection.New();

            //Read the entire section
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Item.Element))
                {
                    GuidanceItemFileReference reference = GuidanceItemFileReference.New();
                    reference.Id = reference.FileName = reader.ReadElementContentAsString();
                    reference.CSSFile = ModelStorageService.DefaultCSSFileName;
                    items.Add(reference.Id, reference);
                }
            }

            return items;
        }

        /// <summary>
        /// Fills in the images in a guidance item file reference
        /// </summary>
        /// <param name="gifr"></param>
        /// <param name="reader"></param>
        public void ConvertXmlReaderToImages(GuidanceItemFileReference gifr, XmlReader reader)
        {
            while (reader.Read() && reader.Name != SchemaHelper.GuidanceItem.Images)
            {
                reader.MoveToAttribute(SchemaHelper.Images.name);
                string name = reader.Value;
                reader.Read();
                string img = reader.ReadContentAsString();
                gifr.AllImages[name] = img;
            }
        }
        
        /// <summary>
        /// Process the folder definition node.
        /// </summary>
        /// <param name="reader">The XmlReader positioned at the folder node.</param>
        /// <returns>A new instance of a folder.</returns>
        private Folder ConvertXmlReaderToFolder(XmlReader reader)
        {
            // Create a new folder
            Folder folder = Folder.New();

            //If there is something in the reader.
            if (reader.Read())
            {
                // Set the folder properties.
                if (reader.MoveToFirstAttribute())
                {
                    do
                    {
                        if (reader.Name == SchemaHelper.Folder.Caption)
                        {
                            folder.Caption = reader.Value;
                        }
                    } 
					while (reader.MoveToNextAttribute());
                }
            }

            // Read the inner nodes.
            while (reader.Read())
            {
                // Process the column definition nodes.
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Columns.Element))
                {
                    folder.Columns = ConvertXmlReaderToColumns(reader.ReadSubtree());
                }
                // Process the Folder definition nodes.
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Folder.Element))
                {
                    Folder newFolder = ConvertXmlReaderToFolder(reader.ReadSubtree());
                    newFolder.ParentFolder = folder;
                    folder.Folders.Add(newFolder.Caption, newFolder);
                }
                // Process the view definition nodes.
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.View.Element))
                {
                    GuidanceExplorer.Model.View newView = ConvertXmlReaderToView(reader.ReadSubtree());
                    newView.ParentFolder = folder;
                    folder.Views.Add(newView.Caption, newView);
                }
                // Process the search view definition nodes.
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.SearchView.Element))
                {
                    SearchView newSearchView = ConvertXmlReaderToSearchView(reader.ReadSubtree());
                    newSearchView.ParentFolder = folder;
                    folder.Views.Add(newSearchView.Caption, newSearchView);
                }
                // Process the checklist definition nodes.
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Checklist.Element))
                {
                    GuidanceExplorer.Model.Checklist newChecklist = ConvertXmlReaderToChecklist(reader.ReadSubtree());
                    newChecklist.ParentFolder = folder;
                    folder.Checklists.Add(newChecklist.Caption, newChecklist);
                }
            }

            return folder;
        }

        /// <summary>
        /// Process the column definition for the folder or the view.
        /// </summary>
        /// <param name="reader">The XmlReader instance positioned in the node.</param>
        /// <returns>A columns collection instance.</returns>
        protected ColumnCollection ConvertXmlReaderToColumns(XmlReader reader)
        {
            // Create the new Columns Collection
            ColumnCollection columns = ColumnCollection.New();

            // Read to the end of the file
            while (reader.Read())
            {
                // If the node is Column
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Column.Element))
                {
                    // Create a new Column definition
                    Column column = Column.New();

                    // Read all the attributes
                    if (reader.MoveToFirstAttribute())
                    {
                        do
                        {
                            if (reader.Name == SchemaHelper.Column.Source)
                            {
                                column.Source = reader.Value;

                                foreach (GuidanceType type in GuidanceType.AllGuidanceTypes)
                                {
                                    foreach (Field field in type.Fields.Values)
                                    {
                                        if (string.Compare(field.Name, column.Source, true) == 0)
                                        {
                                            column.Field = field;
                                            break;
                                        }
                                    }
                                }

                            }
                            else if (reader.Name == SchemaHelper.Column.Caption)
                            {
                                column.Caption = reader.Value;
                            }
                            else if (reader.Name == SchemaHelper.Column.Filter)
                            {
                                column.AutoFilter = reader.Value;
                            }
                            else if (reader.Name == SchemaHelper.Column.Sort)
                            {
                                column.Sort = (System.Windows.Forms.SortOrder)Enum.Parse(typeof(System.Windows.Forms.SortOrder), reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Column.Visible)
                            {
                                column.Visible = XmlConvert.ToBoolean(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Column.Width)
                            {
                                column.Width = XmlConvert.ToInt32(reader.Value);
                            }
                            else if (reader.Name == SchemaHelper.Column.Position)
                            {
                                column.Position = XmlConvert.ToInt32(reader.Value);
                            }
                        } while (reader.MoveToNextAttribute());
                    }

                    if (column.Position == -1)
                    {
                        column.Position = columns.Count;
                    }

                    //If the column field exist in the current GE instance
                    if (column.Field != null)
                    {
                        // Keep the column in the View or Folder.
                        columns.Add(column.Source, column);
                    }
                }
            }
            return columns;
        }

        /// <summary>
        /// Convert the data in the XmlReader into a new instance of a View.
        /// </summary>
        /// <param name="reader">The XmlReader positioned at the begining of the View information.</param>
        /// <returns>A new instance of a View.</returns>
        private GuidanceExplorer.Model.View ConvertXmlReaderToView(XmlReader reader)
        {
            //Create a new view.
            GuidanceExplorer.Model.View newView = GuidanceExplorer.Model.View.New();

            //Read the attributes.
            if (reader.Read())
            {
                if (reader.MoveToFirstAttribute())
                {
                    do
                    {
                        if (reader.Name == SchemaHelper.View.Caption)
                        {
                            newView.Caption = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.View.Id)
                        {
                            newView.Id = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.View.Author)
                        {
                            newView.Author = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.View.Description)
                        {
                            newView.Description = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.View.CreationDate)
                        {
                            newView.LastUpdate = XmlConvert.ToDateTime(reader.Value, XmlDateTimeSerializationMode.Utc);
                        }
                        else if (reader.Name == SchemaHelper.View.RootView)
                        {
                            newView.RootView = XmlConvert.ToBoolean(reader.Value);
                        }
                    } while (reader.MoveToNextAttribute());
                }
            }

            //Read the inner nodes.
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Columns.Element))
                {
                    newView.Columns = ConvertXmlReaderToColumns(reader.ReadSubtree());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Items.Element))
                {
                    GuidanceItemCollection items = ConvertXmlReaderToGuidanceItemCollection(reader.ReadSubtree());
                    if (newView.CanSetItems)
                    {
                        newView.Items = items;
                    }
                }
            }

            return newView;
        }


        /// <summary>
        /// Convert the data in the XmlReader into a new instance of a View.
        /// </summary>
        /// <param name="reader">The XmlReader positioned at the begining of the View information.</param>
        /// <returns>A new instance of a View.</returns>
        private Checklist ConvertXmlReaderToChecklist(XmlReader reader)
        {
            //Create a new view.
            Checklist checklist = Checklist.New();

            //Read the attributes.
            if (reader.Read())
            {
                if (reader.MoveToFirstAttribute())
                {
                    do
                    {
                        if (reader.Name == SchemaHelper.Checklist.Caption)
                        {
                            checklist.Caption = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.Checklist.Id)
                        {
                            checklist.Id = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.Checklist.Author)
                        {
                            checklist.Author = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.Checklist.Description)
                        {
                            checklist.Description = reader.Value;
                        }
                        else if (reader.Name == SchemaHelper.Checklist.CreationDate)
                        {
                            checklist.LastUpdate = XmlConvert.ToDateTime(reader.Value, XmlDateTimeSerializationMode.Local);
                        }
                        else if (reader.Name == SchemaHelper.Checklist.RootView)
                        {
                            checklist.RootView = XmlConvert.ToBoolean(reader.Value);
                        }
                    } while (reader.MoveToNextAttribute());
                }
            }

            //Read the inner nodes.
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Columns.Element))
                {
                    checklist.Columns = ConvertXmlReaderToColumns(reader.ReadSubtree());
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Items.Element))
                {
                    GuidanceItemCollection items = ConvertXmlReaderToGuidanceItemCollection(reader.ReadSubtree(), checklist);
                    if (checklist.CanSetItems)
                    {
                        checklist.Items = items;
                    }
                }
            }

            return checklist;
        }

        /// <summary>
        /// Process the items node in the library node. File references will be found in the items node.
        /// </summary>
        /// <param name="xmlReader">The xml reader positioned at the Items node.</param>
        private GuidanceItemCollection ConvertXmlReaderToGuidanceItemCollection(XmlReader reader)
        {
            return ConvertXmlReaderToGuidanceItemCollection(reader, null);
        }

        private GuidanceItemCollection ConvertXmlReaderToGuidanceItemCollection(XmlReader reader, Checklist checklist)
        {
            //Create a new collection.
            GuidanceItemCollection items = GuidanceItemCollection.New();

            //Read the entire section
            while (reader.Read())
            {
                if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.Item.Element))
                {
                    //Create a new item.
                    GuidanceItem reference = GuidanceItem.New();

                    //Read the item id.
                    reference.Id = reader.ReadElementContentAsString();

                    //Add the item to the collection.
                    items.Add(reference.Id, reference);
                }
                else if (SchemaHelper.IsGuidanceExplorerNode(reader, SchemaHelper.GuidanceItem.Element))
                {
                    //Create a new item.
                    GuidanceItemFileReference item = GuidanceItemFileReference.New();
                    ProcessGuidanceItemMetadataElement(item, reader,checklist);
                    //Add the item to the collection.
                    items.Add(item.Id, item);
                    if (checklist != null)
                    {
                        checklist.SetCheck(item.Id, "true".Equals(item[SchemaHelper.ChecklistItem.IsChecked]));
                    }
                }
            }
            return items;
        }

        private void ProcessGuidanceItemMetadataElement(GuidanceItemFileReference reference, XmlReader reader, Checklist checklist)
        {
            if (reader.MoveToFirstAttribute())
            {
                do
                {
                    if (reader.Name == SchemaHelper.GuidanceItem.Id)
                    {
                        reference.Id = reader.Value;
                    }
                    else if (reader.Name == SchemaHelper.GuidanceItem.FilePath)
                    {
                        reference.FileName = reader.Value;
                    }
                    else if (reader.Name == SchemaHelper.GuidanceItem.Title)
                    {
                        reference.Title = reader.Value;
                    }
                    else if (reader.Name == SchemaHelper.GuidanceItem.Type)
                    {
                        reference.GuidanceType = GuidanceType.Get(reader.Value);
                    }

                    else if (reader.Name == SchemaHelper.GuidanceItem.CSSFile)
                    {
                        reference.CSSFile = reader.Value;
                    }
                    else if (reader.Name == SchemaHelper.GuidanceItem.Date)
                    {
                        reference.DateTime = DateTime.Parse(reader.Value, System.Globalization.DateTimeFormatInfo.InvariantInfo);
                    }
                    //else if (reader.Name == SchemaHelper.GuidanceItem.IsChecked)
                    //{
                    //    //TODO: validate that Id is already loaded
                    //    checklist.SetCheck(reference.Id, "true".Equals(reader.Value));
                    //}
                    else if (reader.Name != "xmlns")
                    {
                        reference[reader.Name] = reader.Value;
                    }
                } while (reader.MoveToNextAttribute());
            }
        }
        
		/// <summary>
        /// Renders a Guidance in the model to the set of files in the Libraries folder.
        /// </summary>
        /// <param name="guidance">The Guidance instance to process.</param>
        /// <param name="currentDirectory">The current directory for this process.</param>
        public void SaveGuidance(Guidance guidance, string currentDirectory)
        {
            //Compose the guidance type file path.
            string guidanceTypeFileName = Path.Combine(currentDirectory, GuidanceTypesFile);
            FileInfo guidanceTypeFileInfo = new FileInfo(guidanceTypeFileName);

			//Create a backup for the guidance type file.
            CreateBackup(guidanceTypeFileInfo);

            //Save the guidance type information
            RenderGuidanceTypeToFileInfo(guidanceTypeFileInfo);

            //Create a backup for the user views source file.
            CreateBackup(guidance.UserViews.SourceFileInfo);

            //Create a backup for the global views source file
            CreateBackup(guidance.GlobalViews.SourceFileInfo);

            //Render the UserViews to the same file used for loading it.
            RenderUserViewsToFileInfo(guidance.UserViews, guidance.UserViews.SourceFileInfo);

            //Render the GlobalViews to the same file used for loading it.
            RenderGlobalViewsToFileInfo(guidance.GlobalViews, guidance.GlobalViews.SourceFileInfo);

            //Saves the LibrariesNode column configuration

            if (_guidance.Columns != null)
            {
                string librariesColumns = Path.Combine(LibraryFolder, LibrariesColumnsConfiguration);

                if (File.Exists(librariesColumns))
                {
                    File.Delete(librariesColumns);
                }

                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Indent = true;
                using (XmlWriter writer = XmlTextWriter.Create(librariesColumns, settings))
                {
                    RenderColumnCollectionToXmlWriter(_guidance.Columns, writer);
                }
            }

            //Process the Libraries
            foreach (Library library in guidance.Libraries.Values)
            {
                if (library.RenderAsFile)
                {
                    //Create a backup for the source file.
                    CreateBackup(library.SourceFileInfo);

                    //Render the Library to the same file used for loading it.
                    RenderLibraryToFileInfo(library, library.SourceFileInfo);

                    if (library.FileSubscribed && library.HasChanges)
                    {
                        library.FileSubscribed = false;
                        RenderLibraryToFileInfo(library, library.OriginalSourceFileInfo);
                    }
                }
            }

            //Process the deleted libraries
            foreach (Library library in guidance.DeletedLibraries.Values)
            {
                if (File.Exists(library.SourceFileInfo.FullName))
                {
                    //Delete the library file
                    File.Delete(library.SourceFileInfo.FullName);
                    if (!library.FileSubscribed)
                    {
                        //Delete all the items and
                        foreach (GuidanceItemFileReference fileReference in library.Items.Values)
                        {
                            //If the file exists.
                            if (File.Exists(fileReference.FullFileName))
                            {
                                //Delete the item file
                                File.Delete(fileReference.FullFileName);
                            }
                            else
                            {
                                //The file has been manually deleted or renamed.
                            }
                        }
                        //Delete the library folder
                        DirectoryInfo libFolder = new FileInfo(library.IndexFileName).Directory;
                        if (libFolder.Exists)
                        {
                            libFolder.Delete(true);
                        }
                    }
                }
                else
                {
                    //The file has been manually deleted or renamed.
                }

                //If the library is FileSubscribed, it saves the changes in the source
                if (library.FileSubscribed && library.HasChanges)
                {
                    library.FileSubscribed = false;
                    RenderLibraryToFileInfo(library, library.OriginalSourceFileInfo);
                }
            }

            //Process deleted items
            foreach (GuidanceItem guidanceItem in guidance.DeletedGuidanceItems.Values)
            {
                GuidanceItemFileReference gifr = guidanceItem as GuidanceItemFileReference;
                if (gifr == null)
                {
                    throw new NotImplementedException();
                }

                //Check the existence of the file.
                if (File.Exists(gifr.FullFileName))
                {
                    //Delete the file.
                    File.Delete(gifr.FullFileName);
                }
                else
                {
                    //I don't care if the file does not exist.
                }
            }
        }

		/// <summary>
        /// Render the information from the GuidanceType into a file.
        /// </summary>
        /// <param name="guidanceTypeFileInfo"></param>
        private void RenderGuidanceTypeToFileInfo(FileInfo guidanceTypeFileInfo)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            using (FileStream fileStream = new FileStream(guidanceTypeFileInfo.FullName, FileMode.CreateNew))
            using (XmlWriter writer = XmlTextWriter.Create(fileStream, settings))
            {
                writer.WriteStartDocument();

                writer.WriteStartElement(SchemaHelper.GuidanceTypes.Element, SchemaHelper.GuidanceTypeNamespace);

                foreach (GuidanceType guidanceType in GuidanceType.AllGuidanceTypes)
                {
                    writer.WriteStartElement(SchemaHelper.GuidanceType.Element, SchemaHelper.GuidanceTypeNamespace);

                    writer.WriteAttributeString(SchemaHelper.GuidanceType.Name, guidanceType.Name);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.Id, guidanceType.Id);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.SchemaId, guidanceType.SchemaId);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.DisplayName, guidanceType.DisplayName);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.ListDisplayName, guidanceType.ListDisplayName);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.NewGuidanceItemDisplayName, guidanceType.NewGuidanceTypeDisplayName);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.TemplateFileName, guidanceType.TemplateFileName);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.ImageFileName, guidanceType.ImageFileName);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.ExampleFileName, guidanceType.ExampleFileName);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.NodeDisplayName, guidanceType.NodeDisplayName);
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.IsDeleted, XmlConvert.ToString(guidanceType.IsDeleted));
                    writer.WriteAttributeString(SchemaHelper.GuidanceType.CreationDate, XmlConvert.ToString(guidanceType.DateTime, XmlDateTimeSerializationMode.Utc));

                    writer.WriteStartElement(SchemaHelper.Fields.Element, SchemaHelper.GuidanceTypeNamespace);

                    foreach (Field field in guidanceType.Fields.Values)
                    {
                        writer.WriteStartElement(SchemaHelper.Field.Element, SchemaHelper.GuidanceTypeNamespace);

                        writer.WriteAttributeString(SchemaHelper.Field.Name, field.Name);
                        writer.WriteAttributeString(SchemaHelper.Field.Caption, field.Caption);
                        writer.WriteAttributeString(SchemaHelper.Field.DefaultSort, field.DefaultSort.ToString());
                        writer.WriteAttributeString(SchemaHelper.Field.Type, field.Type.ToString());
                        writer.WriteAttributeString(SchemaHelper.Field.Distinct, XmlConvert.ToString(field.Distinct));
                        writer.WriteAttributeString(SchemaHelper.Field.Editable, XmlConvert.ToString(field.Editable));
                        writer.WriteAttributeString(SchemaHelper.Field.Width, XmlConvert.ToString(field.Width));
                        writer.WriteAttributeString(SchemaHelper.Field.Position, XmlConvert.ToString(field.Position));
                        writer.WriteAttributeString(SchemaHelper.Field.Visible, XmlConvert.ToString(field.Visible));
                        writer.WriteAttributeString(SchemaHelper.Field.ShowInGuidanceTypeNode, XmlConvert.ToString(field.ShowInGuidanceTypeNode));
                        if (field.Values != null)
                        {
                            StringBuilder allValues = new StringBuilder();
                            foreach (string value in field.Values)
                            {
                                if (allValues.Length != 0)
                                {
                                    allValues.Append(",");
                                }
                                allValues.Append(value);
                            }
                            writer.WriteAttributeString(SchemaHelper.Field.Values, allValues.ToString());
                        }
                        writer.WriteEndElement(); //SchemaHelper.Field.Element
                    }

                    writer.WriteEndElement(); //SchemaHelper.Fields.Element

                    writer.WriteEndElement(); //SchemaHelper.GuidanceType.Element
                }

                writer.WriteEndElement(); //SchemaHelper.GuidanceTypes.Element
            }
        }

        private static void UpdateViewsReferences(Library library, Folder folder, Dictionary<string, string> mapping)
        {
            foreach (GuidanceExplorer.Model.View view in folder.Views.Values)
            {
                UpdateViewReference(library, mapping, view);
            }

            foreach (Folder f in folder.Folders.Values)
            {
                UpdateViewsReferences(library, f, mapping);
            }
        }

        private static void UpdateViewReference(Library library, Dictionary<string, string> mapping, GuidanceExplorer.Model.View view)
        {
            if (!(view is SearchView))
            {
                GuidanceItemCollection newItems = GuidanceItemCollection.New();

                foreach (GuidanceItem item in view.Items)
                {
                    string key = GetKeyByValue(mapping, item.Id);
                    if (key != null && library.Items.ContainsKey(key))
                    {
                        GuidanceItem newItem = library.Items[key];
                        newItems.Add(newItem.Id, newItem);
                    }
                    else
                    {
                        newItems.Add(item.Id, item);
                    }
                }
                view.Items = newItems;
            }
        }

        public static string GetKeyByValue(Dictionary<string, string> dic, string value)
        {
            foreach (KeyValuePair<string, string> item in dic)
            {
                if (string.Compare(item.Value, value) == 0)
                {
                    return item.Key;
                }
            }

            return null;
        }
        /// <summary>
        /// Copies item to new library location and updates item filename property
        /// </summary>
        /// <param name="library"></param>
        /// <param name="gifr"></param>
        /// <returns></returns>
        private void CopyItemToNewLibraryLocation(Library library, GuidanceItemFileReference gifr, IGuidanceItemContainer itemContainer)
        {
            Library originalLibrary = itemContainer as Library;
            string originalFileName = string.Empty;

            if (originalLibrary != null && originalLibrary.FileSubscribed)
            {
                originalFileName = Path.GetFullPath(Path.Combine(originalLibrary.OriginalSourceFileInfo.DirectoryName, gifr.FileName));
            }
            else
            {
                originalFileName = gifr.FullFileName;
            }

            gifr.FileName = Path.Combine(library.Name + "\\" + gifr.GuidanceType.Name, Path.GetFileName(gifr.FileName));
            string fullCopyPath = Path.Combine(library.SourceFileInfo.DirectoryName, gifr.FileName);
           
            if (!Directory.Exists(Path.GetDirectoryName(fullCopyPath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(fullCopyPath));
            }

            File.Copy(originalFileName, fullCopyPath, true);
        }
               
        /// <summary>
        /// 
        /// </summary>
        /// <param name="itemContainer"></param>
        /// <param name="name"></param>
        /// <param name="fileInfo"></param>
        /// <param name="htmlService"></param>
        public void ExportAsLibrary(IGuidanceItemContainer itemContainer, String name, FileInfo fileInfo, IHtmlRenderService htmlService)
        {
            //Create a temp library.
            Library tempLibrary = Library.New();
            tempLibrary.Caption = name;
            tempLibrary.Name = name;
            tempLibrary.SourceFileInfo = fileInfo;
            tempLibrary.OriginalSourceFileInfo = fileInfo;
            tempLibrary.ReadProtection = ReadProtection.ReadWrite;

            foreach (GuidanceItem item in itemContainer)
            {
                GuidanceItemFileReference newGifr, gifr = item as GuidanceItemFileReference;

                if (gifr == null)
                {
                    gifr = _guidance.GetGuidanceItemFileReference(item);
                }
                if (!tempLibrary.Items.ContainsKey(gifr.Id))
                {
                    //Create a new instance for saving into a new location.
                    newGifr = gifr.Clone();
                    newGifr.Id = item.Id;
                    newGifr.DateTime = DateTime.Today;

                    CopyItemToNewLibraryLocation(tempLibrary, newGifr, itemContainer);

                    //Add the item to the library
                    tempLibrary.Items.Add(newGifr.Id, newGifr);
                }
            }

            Library originalLibrary = itemContainer as Library;

            if (originalLibrary != null)
            {
                //save views and folders if we are exporting a library
                foreach (Folder folder in originalLibrary.Folders.Values)
                {
                    Folder newFolder = folder.Clone();
                    tempLibrary.Folders.Add(newFolder.Caption, newFolder);
                }
                foreach (GuidanceExplorer.Model.View view in originalLibrary.Views.Values)
                {
                    GuidanceExplorer.Model.View newView = view.Clone();
                    tempLibrary.Views.Add(newView.Caption, newView);
                }
            }            
            //Save the library
            RenderLibraryToFileInfo(tempLibrary, fileInfo);
        }

        /// <summary>
        /// Render the library instance to an FileInfo.
        /// </summary>
        /// <param name="library">The library to render.</param>
        /// <param name="fileInfo">The fileInfo to render to.</param>
        public void RenderLibraryToFileInfo(Library library, FileInfo fileInfo)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;

            if (fileInfo.Exists)
            {
                fileInfo.Delete();
            }

            using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.CreateNew))
            using (XmlWriter writer = XmlTextWriter.Create(fileStream, settings))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement(SchemaHelper.GuidanceExplorer.Element, SchemaHelper.Namespace);

                RenderLibraryToXmlWriter(library, writer);

                writer.WriteEndElement(); //SchemaHelper.GuidanceExplorer.Element
            }
        }

        /// <summary>
        /// Render the instance of the library to an XmlWriter.
        /// </summary>
        /// <param name="library">The library to render.</param>
        /// <param name="writer">The xmlwriter to render to.</param>
        private void RenderLibraryToXmlWriter(Library library, XmlWriter writer)
        {
            writer.WriteStartElement(SchemaHelper.Library.Element, SchemaHelper.Namespace);

            writer.WriteAttributeString(SchemaHelper.Library.Name, library.Name);
            writer.WriteAttributeString(SchemaHelper.Library.Caption, library.Caption);
            writer.WriteAttributeString(SchemaHelper.Library.ReadProtection, library.ReadProtection.ToString());
            writer.WriteAttributeString(SchemaHelper.Library.FileSubscribed, XmlConvert.ToString(library.FileSubscribed));
            writer.WriteAttributeString(SchemaHelper.Library.WebSubscribed, XmlConvert.ToString(library.WebSubscribed));
            if (library.WebSubscribed)
            {
                writer.WriteAttributeString(SchemaHelper.Library.URL, library.URL);
            }

            if (library.FileSubscribed)
            {
                writer.WriteAttributeString(SchemaHelper.Library.OriginalSource, library.OriginalSourceFileInfo.FullName);
            }
            writer.WriteStartElement(SchemaHelper.LibraryStructure.Element, SchemaHelper.Namespace);

            //If the library is subscribed, the just save the heading.
            if (!library.FileSubscribed)
            {
                //Save all root folders.
                foreach (Folder folder in library.Folders.Values)
                {
                    RenderFolderToXmlWriter(folder, writer, false);
                }

                //Save all root views.
                foreach (GuidanceExplorer.Model.View view in library.Views.Values)
                {
                    SearchView searchView = view as SearchView;
                    if (searchView == null)
                    {
                        RenderViewToXmlWriter(view, writer, false);
                    }
                    else
                    {
                        RenderSearchViewToXmlWriter(searchView, writer);
                    }
                }
            }

            writer.WriteEndElement(); //SchemaHelper.LibraryStructure.Element

            //If the library is subscribed, ignore the items.
            if (!library.FileSubscribed)
            {
                //Save all the items.
                string itemsFolder = Path.GetDirectoryName(library.SourceFileInfo.FullName);
                RenderLibraryGuidanceItemCollectionToXmlWriter(itemsFolder, library.Items, writer);
            }
            else
            {
                writer.WriteStartElement(SchemaHelper.Items.Element, SchemaHelper.Namespace);
                writer.WriteEndElement(); //SchemaHelper.Items.Element
            }

            //Render the columns.
            RenderColumnCollectionToXmlWriter(library.Columns, writer);

            writer.WriteEndElement(); //SchemaHelper.Library.Element
        }

        /// <summary>
        /// Create a backup for the file before saving.
        /// </summary>
        /// <param name="fileInfo">The file infor for the target file.</param>
        private void CreateBackup(FileInfo fileInfo)
        {
            //Keep a backup of the old user views file.
            if (fileInfo != null && File.Exists(fileInfo.FullName))
            {
                //Create a backup file.                
                string bakFileName = Path.ChangeExtension(fileInfo.FullName, "xml.bak");

                //Remove old backup file.
                if (File.Exists(bakFileName))
                {
                    File.Delete(bakFileName);
                }

                //Keep old file backup.
                File.Move(fileInfo.FullName, bakFileName);
            }
        }

        /// <summary>
        /// Renders a Guidance instance to a file represented by the FileInfo.
        /// </summary>
        /// <param name="userViews">The userviews instance to render.</param>
        /// <param name="fileInfo">The fileInfo to render to.</param>
        public void RenderUserViewsToFileInfo(UserViews userViews, FileInfo fileInfo)
        {
			if (fileInfo == null)
			{
				return;
			}
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.CreateNew))
            using (XmlWriter writer = XmlTextWriter.Create(fileStream, settings))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement(SchemaHelper.GuidanceExplorer.Element, SchemaHelper.Namespace);

                RenderUserViewsToXmlWriter(userViews, writer);

                writer.WriteEndElement(); //SchemaHelper.GuidanceExplorer.Element
            }
        }

        /// <summary>
        /// Renders a Guidance instance to a file represented by the FileInfo.
        /// </summary>
        /// <param name="userViews">The globalviews instance to render.</param>
        /// <param name="fileInfo">The fileInfo to render to.</param>
        public void RenderGlobalViewsToFileInfo(GlobalViews globalViews, FileInfo fileInfo)
        {
            if (fileInfo == null)
            {
                return;
            }

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.CreateNew))
            using (XmlWriter writer = XmlTextWriter.Create(fileStream, settings))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement(SchemaHelper.GuidanceExplorer.Element, SchemaHelper.Namespace);

                RenderGlobalViewsToXmlWriter(globalViews, writer);

                writer.WriteEndElement(); //SchemaHelper.GuidanceExplorer.Element
            }
        }
        
        /// <summary>
        /// Render the instance of the Guidance to an XmlWriter.
        /// </summary>
        /// <param name="userViews">The userViews to render.</param>
        /// <param name="writer">The xmlwriter to render to.</param>
        private void RenderUserViewsToXmlWriter(UserViews userViews, XmlWriter writer)
        {
            writer.WriteStartElement(SchemaHelper.UserViews.Element, SchemaHelper.Namespace);

            if (userViews.Columns != null)
            {
                RenderColumnCollectionToXmlWriter(userViews.Columns, writer);
            }

            writer.WriteStartElement(SchemaHelper.UserViews.Structure);
            //Save all root folders.
            foreach (Folder folder in userViews.Folders.Values)
            {
                RenderFolderToXmlWriter(folder, writer,false);
            }

            //Save all root views.
            foreach (GuidanceExplorer.Model.View view in userViews.Views.Values)
            {
                SearchView searchView = view as SearchView;
                if (searchView == null)
                {
                    RenderViewToXmlWriter(view, writer,false);
                }
                else
                {
                    RenderSearchViewToXmlWriter(searchView, writer);
                }
            }
            writer.WriteEndElement(); //SchemaHelper.UserViews.Structure
            writer.WriteEndElement(); //SchemaHelper.UserViews.Element
        }

        /// <summary>
        /// Render the instance of the Guidance to an XmlWriter.
        /// </summary>
        /// <param name="globalViews">The globalViews to render.</param>
        /// <param name="writer">The xmlwriter to render to.</param>
        private void RenderGlobalViewsToXmlWriter(GlobalViews globalViews, XmlWriter writer)
        {
            writer.WriteStartElement(SchemaHelper.GlobalViews.Element, SchemaHelper.Namespace);

            if (globalViews.Columns != null)
            {
                RenderColumnCollectionToXmlWriter(globalViews.Columns, writer);
            }

            writer.WriteStartElement(SchemaHelper.GlobalViews.Structure);
            //Save all root folders.
            foreach (Folder folder in globalViews.Folders.Values)
            {
                RenderFolderToXmlWriter(folder, writer, false);
            }

            //Save all root views.
            foreach (GuidanceExplorer.Model.View view in globalViews.Views.Values)
            {
                SearchView searchView = view as SearchView;
                if (searchView == null)
                {
                    RenderViewToXmlWriter(view, writer, false);
                }
                else
                {
                    RenderSearchViewToXmlWriter(searchView, writer);
                }
            }
            writer.WriteEndElement(); //SchemaHelper.GlobalViews.Structure
            writer.WriteEndElement(); //SchemaHelper.GlobalViews.Element
        }

        /// <summary>
        /// Render a search view to the xml writer
        /// </summary>
        /// <param name="searchView"></param>
        /// <param name="writer"></param>
        private void RenderSearchViewToXmlWriter(SearchView searchView, XmlWriter writer)
        {
            writer.WriteStartElement(SchemaHelper.SearchView.Element, SchemaHelper.Namespace);
            writer.WriteAttributeString(SchemaHelper.SearchView.Caption, searchView.Caption);
            writer.WriteAttributeString(SchemaHelper.SearchView.Id, searchView.Id);
            writer.WriteAttributeString(SchemaHelper.SearchView.CreationDate, XmlConvert.ToString(searchView.LastUpdate, XmlDateTimeSerializationMode.Utc));
            writer.WriteAttributeString(SchemaHelper.SearchView.Description, searchView.Description);
            writer.WriteAttributeString(SchemaHelper.SearchView.Author, searchView.Author);
            writer.WriteAttributeString(SchemaHelper.SearchView.ContainerScopeType, searchView.Scope.ToString());
            writer.WriteAttributeString(SchemaHelper.SearchView.RootView, XmlConvert.ToString(searchView.RootView));
            switch (searchView.Scope)
            {
                case SearchViewScope.UserViews:
                case SearchViewScope.Guidance:
                case SearchViewScope.Library:
                    break;
                case SearchViewScope.Folder:
                    Folder folder = searchView.ContainerScope as Folder;
                    writer.WriteAttributeString(SchemaHelper.SearchView.ContainerScope, folder.Caption);
                    break;
                case SearchViewScope.View:
                    GuidanceExplorer.Model.View view = searchView.ContainerScope as GuidanceExplorer.Model.View;
                    writer.WriteAttributeString(SchemaHelper.SearchView.ContainerScope, view.Caption);
                    break;
            }

            //Render the columns.
            RenderColumnCollectionToXmlWriter(searchView.Columns, writer);

            //Render search view criteria.
            RenderSearchViewCriteriaToXmlWriter(searchView.Criteria, writer);

            writer.WriteEndElement(); //SchemaHelper.SearchView.Element
        }

        /// <summary>
        /// Save a criteria to the view definition.
        /// </summary>
        /// <param name="criteria"></param>
        /// <param name="writer"></param>
        public void RenderSearchViewCriteriaToXmlWriter(Criteria criteria, XmlWriter writer)
        {
            writer.WriteStartElement(SchemaHelper.Criteria.Element, SchemaHelper.Namespace);

            FullTextCriteria fullTextCriteria = criteria as FullTextCriteria;
            AndSearchCriteria andCriteria = criteria as AndSearchCriteria;
            OrSearchCriteria orCriteria = criteria as OrSearchCriteria;
            FieldSearchCriteria fieldCriteria = criteria as FieldSearchCriteria;
            TrueSearchCriteria trueCriteria = criteria as TrueSearchCriteria;
            TopSearchCriteria topCriteria = criteria as TopSearchCriteria;
            if (fullTextCriteria != null)
            {
                writer.WriteElementString(fullTextCriteria.GetType().Name, SchemaHelper.Namespace, fullTextCriteria.Text);
            }
            else if (andCriteria != null)
            {
                writer.WriteStartElement(andCriteria.GetType().Name);
                foreach (Criteria c in andCriteria.Criterias)
                {
                    RenderSearchViewCriteriaToXmlWriter(c, writer);
                }
                writer.WriteEndElement();
            }
            else if (orCriteria != null)
            {
                writer.WriteStartElement(orCriteria.GetType().Name);
                foreach (Criteria c in orCriteria.Criterias)
                {
                    RenderSearchViewCriteriaToXmlWriter(c, writer);
                }
                writer.WriteEndElement();
            }
            else if (fieldCriteria != null)
            {
                writer.WriteStartElement(fieldCriteria.GetType().Name);
                writer.WriteAttributeString(SchemaHelper.FieldSearchCriteria.Value, fieldCriteria.Value);
                writer.WriteAttributeString(SchemaHelper.FieldSearchCriteria.SearchTerm, fieldCriteria.SearchTerm.ToString());
                writer.WriteAttributeString(SchemaHelper.FieldSearchCriteria.Field, fieldCriteria.Field.Name);
                writer.WriteEndElement();
            }
            else if (trueCriteria != null)
            {
                writer.WriteStartElement(trueCriteria.GetType().Name);
                writer.WriteEndElement();
            }
            else if (topCriteria != null)
            {
                writer.WriteStartElement(topCriteria.GetType().Name);
                writer.WriteEndElement();
            }
            else
            {
                throw new NotImplementedException();
            }

            writer.WriteEndElement(); //SchemaHelper.Criteria.Element
        }

        /// <summary>
        /// Renders the contents of a folder in the model to an XmlWriter instance.
        /// </summary>
        /// <param name="folder">The folder to render.</param>
        /// <param name="writer">The xmlwriter to render to.</param>
        protected void RenderFolderToXmlWriter(Folder folder, XmlWriter writer, bool byValue)
        {
            writer.WriteStartElement(SchemaHelper.Folder.Element, SchemaHelper.Namespace);
            writer.WriteAttributeString(SchemaHelper.Folder.Caption, folder.Caption);

            //Render the columns.
            RenderColumnCollectionToXmlWriter(folder.Columns, writer);

            //Render child folders.
            foreach (Folder childFolder in folder.Folders.Values)
            {
                RenderFolderToXmlWriter(childFolder, writer, byValue);
            }

            //Render child views.
            foreach (GuidanceExplorer.Model.View childView in folder.Views.Values)
            {
                SearchView searchView = childView as SearchView;
                if (searchView == null)
                {
                    RenderViewToXmlWriter(childView, writer, byValue);
                }
                else
                {
                    RenderSearchViewToXmlWriter(searchView, writer);
                }
            }
            writer.WriteEndElement(); //SchemaHelper.Folder.Element
        }

        /// <summary>
        /// Render an instance of a View into an XmlWriter.
        /// </summary>
        /// <param name="view">The view to render.</param>
        /// <param name="writer">The xmlwriter to render to.</param>
        protected void RenderViewToXmlWriter(GuidanceExplorer.Model.View view, XmlWriter writer,bool byValue)
        {
            writer.WriteStartElement(SchemaHelper.View.Element, SchemaHelper.Namespace);
            writer.WriteAttributeString(SchemaHelper.View.Id, view.Id);
            writer.WriteAttributeString(SchemaHelper.View.Caption, view.Caption);
            writer.WriteAttributeString(SchemaHelper.View.CreationDate, XmlConvert.ToString(view.LastUpdate, XmlDateTimeSerializationMode.Local));
            writer.WriteAttributeString(SchemaHelper.View.Description, view.Description);
            writer.WriteAttributeString(SchemaHelper.View.Author, view.Author);
            writer.WriteAttributeString(SchemaHelper.View.RootView, XmlConvert.ToString(view.RootView));

            //Render the columns.
            RenderColumnCollectionToXmlWriter(view.Columns, writer);

            //Render view items.
            RenderViewGuidanceItemCollectionToXmlWriter(view.Items, writer,byValue);

            writer.WriteEndElement(); //SchemaHelper.View.Element
        }




		/// <summary>
		/// Render the item collection for a view into an XmlWriter.
		/// </summary>
		/// <param name="guidanceItemCollection">The GuidanceItemCollection to render.</param>
		/// <param name="writer">The xmlwriter to render to.</param>
        private void RenderViewGuidanceItemCollectionToXmlWriter(GuidanceItemCollection guidanceItemCollection, XmlWriter writer, bool byValue)
        {
            writer.WriteStartElement(SchemaHelper.Items.Element, SchemaHelper.Namespace);

            //Writer all the item Ids.
            foreach (GuidanceItem item in guidanceItemCollection.Values)
            {
                //serialize the item by value (need all information)
                if (byValue)
                {
                    writer.WriteStartElement(SchemaHelper.GuidanceItem.Element, SchemaHelper.Namespace);

                    GuidanceItemFileReference fileRef = item as GuidanceItemFileReference;
                    if (fileRef != null)
                    {
                        writer.WriteAttributeString(SchemaHelper.GuidanceItem.FilePath,fileRef.FileName);
                    }
                    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Id, item.Id);
                    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Type, item.GuidanceType.Name);
                    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Title, item.Title);
                    writer.WriteAttributeString(SchemaHelper.GuidanceItem.CSSFile, item.CSSFile);
                    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Date,XmlConvert.ToString(item.DateTime, XmlDateTimeSerializationMode.RoundtripKind));

                    //Write the custom attributes.
                    foreach (string fieldKey in item.CustomData.Keys)
                    {
                        if (!String.Equals(fieldKey, SchemaHelper.GuidanceItem.Title, StringComparison.InvariantCultureIgnoreCase)
                            //don't write again file path
                            && (fileRef != null && !String.Equals(fieldKey, SchemaHelper.GuidanceItem.FilePath, StringComparison.InvariantCultureIgnoreCase))
                            && !String.Equals(fieldKey,SchemaHelper.ChecklistItem.IsChecked,StringComparison.InvariantCultureIgnoreCase))                           
                        {
							writer.WriteAttributeString(fieldKey, item.CustomData[fieldKey]);
                        }
                    }

                    writer.WriteEndElement();//SchemaHelper.GuidanceItem.Element
                }
                else // Save only the id
                {
                    writer.WriteElementString(SchemaHelper.Item.Element, SchemaHelper.Namespace, item.Id);
                }
            }

            writer.WriteEndElement(); //SchemaHelper.Items.Element
        }

        /// <summary>
        /// Render the item collection for a view into an XmlWriter.
        /// </summary>
        /// <param name="itemsFolder">The folder where the items resides.</param>
        /// <param name="guidanceItemCollection">The GuidanceItemCollection to render.</param>
        /// <param name="writer">The xmlwriter to render to.</param>
        private void RenderLibraryGuidanceItemCollectionToXmlWriter(string itemsFolder, GuidanceItemCollection guidanceItemCollection, XmlWriter writer)
        {
            writer.WriteStartElement(SchemaHelper.Items.Element, SchemaHelper.Namespace);

            //Writer all the item FileName.
            foreach (GuidanceItem item in guidanceItemCollection.Values)
            {
                GuidanceItemFileReference reference = item as GuidanceItemFileReference;
                if (reference != null)
                {
					//string basePath = Path.GetFullPath(itemsFolder) + Path.DirectorySeparatorChar;
					//string relPath = Path.GetFullPath(reference.FileName).Replace(basePath, String.Empty);
					//writer.WriteElementString(SchemaHelper.Item.Element, SchemaHelper.Namespace, relPath);
					writer.WriteElementString(SchemaHelper.Item.Element, SchemaHelper.Namespace, reference.FileName);
                }
                else
                {
                    throw new NotSupportedException();
                }
            }

            writer.WriteEndElement(); //SchemaHelper.Items.Element
        }

        /// <summary>
        /// Render the columns collection into an XmlWriter.
        /// </summary>
        /// <param name="columns">The columns collection instance to render.</param>
        /// <param name="writer">The XmlWriter to render to.</param>
        protected void RenderColumnCollectionToXmlWriter(ColumnCollection columns, XmlWriter writer)
        {
            writer.WriteStartElement(SchemaHelper.Columns.Element, SchemaHelper.Namespace);
            foreach (Column column in columns.Values)
            {
                writer.WriteStartElement(SchemaHelper.Column.Element, SchemaHelper.Namespace);
                writer.WriteAttributeString(SchemaHelper.Column.Source, column.Source);
                writer.WriteAttributeString(SchemaHelper.Column.Caption, column.Caption);
                writer.WriteAttributeString(SchemaHelper.Column.Sort, column.Sort.ToString());
                writer.WriteAttributeString(SchemaHelper.Column.Visible, XmlConvert.ToString(column.Visible));
                writer.WriteAttributeString(SchemaHelper.Column.Width, XmlConvert.ToString(column.Width));
                writer.WriteAttributeString(SchemaHelper.Column.Position, XmlConvert.ToString(column.Position));

                if (!string.IsNullOrEmpty(column.AutoFilter))
                {
                    writer.WriteAttributeString(SchemaHelper.Column.Filter, column.AutoFilter);
                }

                writer.WriteEndElement(); //SchemaHelper.Column.Element
            }
            writer.WriteEndElement(); //SchemaHelper.Columns.Element
        }

        /// <summary>
        /// Finds an item across all libraries by using it's Id.
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public GuidanceItem FindItemById(string id)
        {
            foreach (Library library in _guidance.Libraries.Values)
            {
                GuidanceItem item = library.Items.FindItemById(id);
                if (item != null)
                {
                    return item;
                }
            }
            return null;
        }

        public void RenderGuidanceItemToHtmlFileInfo(GuidanceItemFileReference guidanceItem, FileInfo fileInfo, Stream content)
        {
            using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.OpenOrCreate))
            using (StreamWriter stream = new StreamWriter(fileStream))
            {
                using (StreamReader reader = new StreamReader(content))
                {
                    stream.WriteLine(reader.ReadToEnd());
                }
            }
        }

        /// <summary>
        /// Render an instance of a GuidanceItem to it's format in Xml.
        /// </summary>
        /// <param name="guidanceItem">The instance of the Guidance Item.</param>
        /// <param name="fileInfo">The file where it must be saved.</param>
        /// <param name="htmlContent">The HTML content for the item.</param>
        public void RenderGuidanceItemToFileInfo(GuidanceItemFileReference guidanceItem, FileInfo fileInfo, string htmlContent)
        {
            UpdateIndex(guidanceItem, false);
            using (FileStream fileStream = new FileStream(fileInfo.FullName, FileMode.Create))
            using (XmlWriter writer = XmlTextWriter.Create(fileStream))
            {
                RenderGuidanceItemToXmlWriter(guidanceItem, htmlContent, writer);
            }
        }

        /// <summary>
        /// Updates the index with the guidance item metadata.
        /// </summary>
        /// <param name="guidanceItem">The guidance item that was updated.</param>
        /// <param name="deleted">Wether the item was deleted or updated.</param>
        public void UpdateIndex(GuidanceItemFileReference guidanceItem, bool deleted)
        {
            bool found = false;

            if (guidanceItem.Library == null || !File.Exists(guidanceItem.Library.IndexFileName))
            {
                return;
            }

            XmlDocument document = new XmlDocument();
            XmlNamespaceManager nsm = new XmlNamespaceManager(document.NameTable);
            nsm.AddNamespace("gi", "urn:microsoft:guidanceexplorer:guidanceItem");
            document.Load(guidanceItem.Library.IndexFileName);
            XmlNodeList nodeList = document.SelectNodes("//gi:guidanceItem[@id = '" + guidanceItem.Id + "']", nsm);
            //XmlNodeList nodeList2 = document.SelectNodes("//gi:guidanceItem[@id = '" + guidanceItem.Id.ToUpper() + "']", nsm);

            //foreach (XmlNode elem in nodeList2)
            //{
            //    //there should be no items in upper caps, remove them if found
            //    XmlNode indexElem = document.GetElementsByTagName("index")[0];
            //    indexElem.RemoveChild(elem);
            //}

            if (nodeList.Count > 0)
            {
                found = true;
            }
            else if (nodeList.Count > 1)
            {
                for (int i = 1; i < nodeList.Count; i++)
                {
                    //there should only be one of each id, clean up any extra
                    XmlNode indexElem = document.GetElementsByTagName("index")[0];
                    XmlNode elem = nodeList[i];
                    indexElem.RemoveChild(elem);
                }
            }
            if (deleted)
            {
                if (found)
                {
                    XmlNode indexElem = document.GetElementsByTagName("index")[0];
                    XmlNode elem = nodeList[0];
                    indexElem.RemoveChild(elem);
                }
            }
            else
            {
                if (found)
                {
                    XmlNode elem = nodeList[0];

                    elem.Attributes[SchemaHelper.GuidanceItem.Id].Value = guidanceItem.Id.ToLower();
                    elem.Attributes[SchemaHelper.GuidanceItem.Type].Value = guidanceItem.GuidanceType.Name;
                    elem.Attributes[SchemaHelper.GuidanceItem.Title].Value = guidanceItem.Title;
                    elem.Attributes[SchemaHelper.GuidanceItem.CSSFile].Value = guidanceItem.CSSFile;
                    elem.Attributes[SchemaHelper.GuidanceItem.Date].Value = XmlConvert.ToString(guidanceItem.DateTime, XmlDateTimeSerializationMode.Utc);

                    //Write the custom attributes.
                    foreach (string fieldKey in guidanceItem.CustomData.Keys)
                    {
                        if (!String.Equals(fieldKey, SchemaHelper.GuidanceItem.Title, StringComparison.InvariantCultureIgnoreCase))
                        {
                            if (elem.Attributes[fieldKey] == null)
                            {
                                elem.Attributes.Append(document.CreateAttribute(fieldKey));
                            }
                            elem.Attributes[fieldKey].Value = guidanceItem.CustomData[fieldKey];
                        }
                    }
                }
                else
                {
                    XmlNode indexElem = document.GetElementsByTagName("index")[0];
                    XmlNode guidanceItemElem = indexElem.AppendChild(document.CreateElement(SchemaHelper.GuidanceItem.Element, SchemaHelper.GuidanceItemNamespace));

                    guidanceItemElem.Attributes.Append(document.CreateAttribute(SchemaHelper.GuidanceItem.FilePath)).Value = guidanceItem.FileName;
                    guidanceItemElem.Attributes.Append(document.CreateAttribute(SchemaHelper.GuidanceItem.Id)).Value = guidanceItem.Id.ToLower();
                    guidanceItemElem.Attributes.Append(document.CreateAttribute(SchemaHelper.GuidanceItem.Type)).Value = guidanceItem.GuidanceType.Name;
                    guidanceItemElem.Attributes.Append(document.CreateAttribute(SchemaHelper.GuidanceItem.Title)).Value = guidanceItem.Title;
                    guidanceItemElem.Attributes.Append(document.CreateAttribute(SchemaHelper.GuidanceItem.CSSFile)).Value = Path.GetFileName(guidanceItem.CSSFile);
                    guidanceItemElem.Attributes.Append(document.CreateAttribute(SchemaHelper.GuidanceItem.Date)).Value = XmlConvert.ToString(guidanceItem.DateTime, XmlDateTimeSerializationMode.Utc);

                    foreach (string fieldKey in guidanceItem.CustomData.Keys)
                    {
                        if (!String.Equals(fieldKey, SchemaHelper.GuidanceItem.Title, StringComparison.InvariantCultureIgnoreCase) &&
                            !String.Equals(fieldKey, SchemaHelper.GuidanceItem.FilePath, StringComparison.InvariantCultureIgnoreCase))
                        {
                            if (guidanceItemElem.Attributes[fieldKey] == null)
                            {
                                XmlAttribute attrib = document.CreateAttribute(fieldKey);
                                attrib.Value = guidanceItem.CustomData[fieldKey];
                                guidanceItemElem.Attributes.Append(attrib);
                            }
                        }
                    }
                }
            }
            document.Save(guidanceItem.Library.IndexFileName);
        }

        /// <summary>
        /// Renders the images in an item to XML
        /// </summary>
        /// <param name="?"></param>
        /// <param name="writer"></param>
        public void RenderGuidanceItemImagesToXmlWriter(Dictionary<string, string> allImages, XmlWriter writer)
        {
            if (allImages.Count > 0)
            {
                writer.WriteStartElement(SchemaHelper.GuidanceItem.Images);

                foreach (KeyValuePair<string, string> img in allImages)
                {
                    writer.WriteStartElement(SchemaHelper.Images.Element);
                    writer.WriteAttributeString(SchemaHelper.Images.name, img.Key);
                    writer.WriteString(img.Value);
                    writer.WriteEndElement();
                }

                writer.WriteEndElement();
            }
        }

		/// <summary>
		/// Render an instance of a GuidanceItem to it's format in Xml.
		/// </summary>
		/// <param name="guidanceItem"></param>
		/// <param name="htmlContent"></param>
		/// <param name="writer"></param>
		public void RenderGuidanceItemToXmlWriter(GuidanceItemFileReference guidanceItem, string htmlContent, XmlWriter writer)
		{
			writer.WriteStartDocument();

			//Write the root element
			writer.WriteStartElement(SchemaHelper.GuidanceItem.Element, SchemaHelper.GuidanceItemNamespace);

			//Write the root attribute.
			writer.WriteAttributeString(SchemaHelper.GuidanceItem.Id, guidanceItem.Id);
			writer.WriteAttributeString(SchemaHelper.GuidanceItem.Type, guidanceItem.GuidanceType.Name);
			writer.WriteAttributeString(SchemaHelper.GuidanceItem.Title, guidanceItem.Title);
			writer.WriteAttributeString(SchemaHelper.GuidanceItem.CSSFile, guidanceItem.CSSFile);
			writer.WriteAttributeString(SchemaHelper.GuidanceItem.Date, XmlConvert.ToString(guidanceItem.DateTime, XmlDateTimeSerializationMode.Utc));

			//Write the custom attributes.
			foreach (string fieldKey in guidanceItem.CustomData.Keys)
			{
				if (!String.Equals(fieldKey, SchemaHelper.GuidanceItem.Title, StringComparison.InvariantCultureIgnoreCase))
				{
					writer.WriteAttributeString(fieldKey, guidanceItem.CustomData[fieldKey]);
				}
			}

            RenderGuidanceItemImagesToXmlWriter(guidanceItem.AllImages, writer);

			//Write the content element
			writer.WriteStartElement(SchemaHelper.GuidanceItem.Content, SchemaHelper.GuidanceItemNamespace);

			//Write the HTML content.
			writer.WriteCData(htmlContent);

			writer.WriteEndElement(); //SchemaHelper.GuidanceItem.Content

			writer.WriteEndElement(); //SchemaHelper.GuidanceItem.Element
		}

		/// <summary>
		/// Render an instance of a GuidanceItem to it's format in Xml.
		/// </summary>
        //protected void RenderGuidanceItemToXmlWriter(GuidanceItemFileReference guidanceItem, XmlWriter writer)
        //{
        //    //writer.WriteStartDocument();

        //    //Write the root element
        //    writer.WriteStartElement(SchemaHelper.GuidanceItem.Element, SchemaHelper.GuidanceItemNamespace);

        //    //Write the root attribute.
        //    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Id, guidanceItem.Id);
        //    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Type, guidanceItem.GuidanceType.Name);
        //    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Title, guidanceItem.Title);
        //    writer.WriteAttributeString(SchemaHelper.GuidanceItem.CSSFile, guidanceItem.CSSFile);
        //    writer.WriteAttributeString(SchemaHelper.GuidanceItem.Date, XmlConvert.ToString(guidanceItem.DateTime, XmlDateTimeSerializationMode.RoundtripKind));

        //    //Write the custom attributes.
        //    foreach (string fieldKey in guidanceItem.CustomData.Keys)
        //    {
        //        if (!String.Equals(fieldKey, SchemaHelper.GuidanceItem.Title, StringComparison.InvariantCultureIgnoreCase))
        //        {
        //            writer.WriteAttributeString(fieldKey, guidanceItem.CustomData[fieldKey]);
        //        }
        //    }

        //    //Write the content element
        //    writer.WriteStartElement(SchemaHelper.GuidanceItem.Content, SchemaHelper.GuidanceItemNamespace);

        //    writer.WriteEndElement(); //SchemaHelper.GuidanceItem.Content

        //    writer.WriteEndElement(); //SchemaHelper.GuidanceItem.Element
        //}

        /// <summary>
        /// Removes the guidance from the local store.
        /// </summary>
        /// <param name="guidanceItem"></param>
        /// <param name="library"></param>
        public void DeleteGuidanceItem(GuidanceItem guidanceItem, Library library)
        {
            library.Items.Remove(guidanceItem.Id);

            GuidanceItemFileReference gifr = guidanceItem as GuidanceItemFileReference;
            if (gifr != null)
            {
                UpdateIndex(gifr, true);
                if (File.Exists(gifr.FullFileName))
                {
                    File.Delete(gifr.FullFileName);
                }
            }
        }

        /// <summary>
        /// Remove the guidance item from the view.
        /// </summary>
        /// <param name="view"></param>
        /// <param name="guidanceItem"></param>
        public void DeleteGuidanceItemFromView(GuidanceExplorer.Model.View view, GuidanceItem guidanceItem)
        {
            if (view.HasItems)
            {
                view.Items.Remove(guidanceItem.Id);
                view.LastUpdate = DateTime.UtcNow;
            }
			if (view.CopyItemsByValue && !HasLink(guidanceItem as GuidanceItemFileReference)) //it is a view under the SOlutionNode, we should also remove the file.
            {
				GuidanceItemFileReference gifr = guidanceItem as GuidanceItemFileReference;
                if (gifr != null)
                {
                    if (File.Exists(gifr.FullFileName))
                    {
                        File.Delete(gifr.FullFileName);
                    }
                }
            }
        }

		public bool HasLink(GuidanceItemFileReference gifr)
		{
			if (gifr != null)
			{
				foreach (KeyValuePair<string, Checklist> checkList in _guidance.SolutionViews.Checklists)
				{
					if ( HasLink(gifr, checkList.Value) ) return true;
				}

				foreach (KeyValuePair<string, GuidanceExplorer.Model.View> view in _guidance.SolutionViews.Views)
				{
					if (HasLink(gifr, view.Value)) return true;
				}

				foreach (KeyValuePair<string, GuidanceExplorer.Model.Folder> folder in _guidance.SolutionViews.Folders)
				{
					if (HasLink(gifr, folder.Value)) return true;
				}
			}

			return false;
		}
		private bool HasLink(GuidanceItemFileReference gifr, Checklist checkList)
		{
			foreach (GuidanceItem gItem in checkList.Items)
			{
				if ((gItem as GuidanceItemFileReference) != null)
				{
					if (gifr.FileName.Equals(((GuidanceItemFileReference)gItem).FileName)) return true;
				}

			}
			return false;
		}
		private bool HasLink(GuidanceItemFileReference gifr, GuidanceExplorer.Model.View view)
		{
			foreach (GuidanceItem gItem in view.Items)
			{
				if ((gItem as GuidanceItemFileReference ) != null)
				{
					if (gifr.FileName.Equals(((GuidanceItemFileReference)gItem).FileName)) return true;
				}

			}
			return false;
		}
		private bool HasLink(GuidanceItemFileReference gifr, Folder folder)
		{
			if (folder.Views.Count == 0 && folder.Checklists.Count == 0 && folder.Folders.Count == 0)
			{
				// This folder do not have childs. We are in the end of solution tree. Then we have
				// to return false.
				return false;
			}
			else
			{
				foreach (KeyValuePair<string, Checklist> checkList in folder.Checklists)
				{
					if (HasLink(gifr, checkList.Value)) return true;
				}

				foreach (KeyValuePair<string, GuidanceExplorer.Model.View> view in folder.Views)
				{
					if (HasLink(gifr, view.Value)) return true;
				}

				foreach (KeyValuePair<string, GuidanceExplorer.Model.Folder> folderSearch in folder.Folders)
				{
					if (HasLink(gifr, folderSearch.Value)) return true;
				}

				return false;
			}

		}

        public void AddGuidanceItemToView(GuidanceExplorer.Model.View view, GuidanceItem guidanceItem)
        {
            view.Items.Add(guidanceItem.Id, guidanceItem);
            view.LastUpdate = DateTime.UtcNow;
        }
		
		public void AddGuidanceItemToCheckList(Checklist checkList, GuidanceItem guidanceItem)
		{
			checkList.Items.Add(guidanceItem.Id, guidanceItem);
			checkList.LastUpdate = DateTime.UtcNow;
		}

        public void AddGuidanceItemToLibrary(Library library, GuidanceItem guidanceItem)
        {
            library.Items.Add(guidanceItem.Id, guidanceItem);
            UpdateIndex(guidanceItem as GuidanceItemFileReference, false);
        }

        /// <summary>
        /// Removes the guidance item recursivelly from all the views within folders.
        /// </summary>
        /// <param name="folderCollection"></param>
        /// <param name="guidanceItem"></param>
        public void RemoveGuidanceItemFromAllViews(FolderCollection folderCollection, GuidanceItem guidanceItem)
        {
            foreach (Folder folder in folderCollection.Values)
            {
                foreach (GuidanceExplorer.Model.View view in folder.Views.Values)
                {
                    DeleteGuidanceItemFromView(view, guidanceItem);
                }
                RemoveGuidanceItemFromAllViews(folder.Folders, guidanceItem);
            }
        }

        /// <summary>
        /// Removes the guidance item recursivelly from all the views within all the folders.
        /// </summary>
        /// <param name="library"></param>
        /// <param name="guidanceItem"></param>
        public void RemoveGuidanceItemFromLibrary(Library library, GuidanceItem guidanceItem)
        {
            foreach (GuidanceExplorer.Model.View view in library.Views.Values)
            {
                DeleteGuidanceItemFromView(view, guidanceItem);
            }
            RemoveGuidanceItemFromAllViews(library.Folders, guidanceItem);

            UpdateIndex(guidanceItem as GuidanceItemFileReference, true);
        }
    }
}

